package com.atguigu.gulimall.product.web;

import com.atguigu.gulimall.product.config.TempRedisSonConfig;
import com.atguigu.gulimall.product.entity.CategoryEntity;
import com.atguigu.gulimall.product.service.CategoryService;
import com.atguigu.gulimall.product.vo.Catelog2Vo;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration;
import javafx.beans.binding.ObjectExpression;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

@Controller
public class IndexController {



    @Autowired
    CategoryService categoryService;


    @GetMapping({"/","index.html"})
    public String indexPage(Model model){

        //查出所有1级分类
        List<CategoryEntity> categoryEntities=categoryService.getLeavelFirstCategorys();


        model.addAttribute("categorys",categoryEntities);
        return "index";
    }


    /**
     * TODO 产生对外内存溢出：OutOfDirectMeoryError
     * 1、springboot2.0以后默认使用lettuce作为操作redis的客户端。它是用netty进行网络通信
     * 2、lettuce的bug导致netty堆外内存溢出  如果没有指定堆外内存，默认会使用springboot设置的内存
     *
     * 解决方案：不能使用Dio.netty.maxDirectMemory只去调大堆外内存
     * 1.升级lettuce客户端（缺点：使用netty）   2.切换使用jedis（缺点：老版，长时间未更新）
     * @return
     */

    @ResponseBody
    @GetMapping("/index/catalog.json")
    public Map<String, List<Catelog2Vo>>  getCatalogJson(){

        Map<String, List<Catelog2Vo>> catalogJson = categoryService.getCatalogJson();

        return catalogJson;
    }

    @ResponseBody
    @GetMapping("/hello")
    public String hello() throws IOException {
        RedissonClient redisSon= TempRedisSonConfig.getRedisSon();
        //获取一把锁，只要锁名一样，就是同一把锁
        RLock lock = redisSon.getLock("lock");

        //加锁，默认30s
//        lock.lock();
        //1.锁的自动续期，如果业务超时，运行期间自动给锁续上新的30s
        //2.加锁的业务只要运行完成，就不会给当前锁续期，即使不手动解锁，锁默认在30s以后自动删除


        lock.lock(10, TimeUnit.SECONDS);
        /**
         * 问题：lock.lock(10,TimeUnit.SECONDS);在锁时间到了以后，不会自动续期。
         * 1.如果传递了锁的超时时间，就发送给redis执行脚本，进行占锁，默认超时就是我们制定的时间
         * 2.如果为指定超时时间，就是要 30*1000[lockWachdogTimeout(看门狗的默认时间)];
         *      只要占锁成功，就会启动一个定时任务【重新给锁设置国球时间，新的过期时间就是看门狗的默认时间】
         *      internalLockLeaseTime[看门狗时间] / 3,即10s
         *
         */
        try{
            System.out.println("加锁成功........"+Thread.currentThread().getId());
            Thread.sleep(30000);

        } catch (InterruptedException e) {

        } finally {

            //解锁  不会出现死锁问题
            System.out.println("解锁成功。。。。。。");
            lock.unlock();
        }

        return "hello";
    }

    @ResponseBody
    @GetMapping("/park")
    public String park() throws InterruptedException, IOException {
        RedissonClient redisSon = TempRedisSonConfig.getRedisSon();
        RSemaphore semaphore=redisSon.getSemaphore("park");
        semaphore.acquire();  //获取一个信号，如果没有空余的信号则等待有信号释放后再继续执行
        return "ok";
    }

    @ResponseBody
    @GetMapping("/go")
    public String go() throws IOException, InterruptedException {
        RedissonClient redisSon = TempRedisSonConfig.getRedisSon();
        RSemaphore semaphore=redisSon.getSemaphore("park");
        semaphore.release();  //释放一个信号
        return "ok";
    }
}

//232+100+808+